// SPDX-License-Identifier: GPL-2.0-or-later



#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>

#include <QClipboard>
#include <QDesktopServices>
#include <QDialog>
#include <QFileDialog>
#include <QFontDialog>
#include <QFontMetrics>
#include <QInputDialog>
#include <QImageReader>
#include <QMenu>
#include <QMessageBox>
#include <QStatusBar>
#include <QTextEdit>
#include <QTimer>
#include <QUrl>
#include <QtScript/QScriptValue>
#include <QMdiArea>
#include <QMdiSubWindow>
#include <QTemporaryFile>
#include <QRegExp>
#include <QNetworkProxy>
#include "addrdialog.h"
#include "articledialog.h"
#include "msgdialog.h"
#include "osdmessage.h"
#include "popwidget.h"
#include "progressBar.h"
#include "common.h"
#include "fqterm_buffer.h"
#include "fqterm_config.h"
#include "fqterm_filedialog.h"
#include "fqterm_frame.h"
#include "fqterm_http.h"
#include "fqterm_ip_location.h"
#include "fqterm_param.h"
#include "fqterm_path.h"
#include "fqterm_screen.h"
#include "fqterm_session.h"
#include "fqterm_sound.h"
#include "fqterm_window.h"
#include "sshlogindialog.h"
#include "statusBar.h"
#include "zmodemdialog.h"

#include "fqterm_scriptengine.h"


namespace FQTerm {

char FQTermWindow::directions_[][5] =  {
    // 4
    "\x1b[1~",  // 0 HOME
    "\x1b[4~",  // 1 END
    "\x1b[5~",  // 2 PAGE UP
    "\x1b[6~",  // 3 PAGE DOWN
    // 3
    "\x1b[A",  // 4 UP
    "\x1b[B",  // 5 DOWN
    "\x1b[D",  // 6 LEFT
    "\x1b[C" // 7 RIGHT
};
/*
  class FAQ: public QObject {
  Q_OBJECT;
  public slots:
  FAQ() {a = 100;}

  int get() {return a;}
  
  private:
  int a;
  };
*/

//constructor
FQTermWindow::FQTermWindow(FQTermConfig *config, FQTermFrame *frame, FQTermParam param,
                           int addressIndex, QWidget *parent,
                           const char *name, Qt::WindowFlags wflags)
    : QMainWindow(parent, wflags),
      frame_(frame),
      isSelecting_(false),
      addressIndex_(addressIndex),
      sound_(NULL),
      isMouseClicked_(false),
      blinkStatus_(true),
      isUrlUnderLined_(false),
      script_engine_(NULL),
      externalEditor_(NULL)
{
    // isMouseX11_ = false;
    session_ = new FQTermSession(config, param);
    screen_ = new FQTermScreen(this, session_);

    config_ = config;
    zmodemDialog_ = new zmodemDialog(this);

    externalEditor_ = new FQTermExternalEditor(this);

    pageViewMessage_ = new PageViewMessage(this);
    tabBlinkTimer_ = new QTimer;

    setWindowIcon(QIcon("noicon"));
    setWindowTitle(param.name_);
    setMouseTracking(true);
    setFocusProxy(screen_);
    setCentralWidget(screen_);
    addMenu();
    setStatusBar(nullptr);

    FQ_VERIFY(connect(pageViewMessage_, SIGNAL(hideAt(const QRect&)), screen_, SLOT(widgetHideAt(const QRect&))));

    FQ_VERIFY(connect(frame_, SIGNAL(bossColor()), screen_, SLOT(bossColor())));

    FQ_VERIFY(connect(frame_, SIGNAL(fontAntiAliasing(bool)), screen_, SLOT(setFontAntiAliasing(bool))));

    FQ_VERIFY(connect(frame_, SIGNAL(changeLanguage()), this, SLOT(recreateMenu())));
    FQ_VERIFY(connect(frame_, SIGNAL(bossColor()), screen_, SLOT(bossColor())));
    FQ_VERIFY(connect(frame_, SIGNAL(updateScroll()), screen_, SLOT(updateScrollBar())));

    FQ_VERIFY(connect(screen_, SIGNAL(inputEvent(const QString&)), session_, SLOT(handleInput(const QString&))));

    FQ_VERIFY(connect(session_, SIGNAL(zmodemStateChanged(int, int, const char*)), this, SLOT(ZmodemState(int, int, const char *))));
    FQ_VERIFY(connect(zmodemDialog_, SIGNAL(canceled()), session_, SLOT(cancelZmodem())));
    FQ_VERIFY(connect(session_, SIGNAL(connectionClosed()), this, SLOT(connectionClosed())));
    FQ_VERIFY(connect(session_, SIGNAL(startAlert()), this, SLOT(startBlink())));
    FQ_VERIFY(connect(session_, SIGNAL(stopAlert()), this, SLOT(stopBlink())));
    // FQ_VERIFY(connect(session_->decoder_, SIGNAL(mouseMode(bool)),
    //                   this, SLOT(setMouseMode(bool))));
    FQ_VERIFY(connect(session_, SIGNAL(articleCopied(int, const QString)), this, SLOT(articleCopied(int, const QString))));
    FQ_VERIFY(connect(session_, SIGNAL(requestUserPwd(QString*, QString*, bool*)), this, SLOT(requestUserPwd(QString *, QString *, bool *))));
    FQ_VERIFY(connect(session_, SIGNAL(warnInsecure(const QString &, bool*)),
                      this, SLOT(warnInsecure(const QString &, bool *))));
    //connect telnet signal to slots
    // QVERIFY(connect(session_->telnet_, SIGNAL(readyRead(int)),
    //                 this, SLOT(readReady(int))));
    FQ_VERIFY(connect(session_, SIGNAL(sessionUpdated()), this, SLOT(sessionUpdated())));
    FQ_VERIFY(connect(session_, SIGNAL(bellReceived()), this, SLOT(beep())));
    FQ_VERIFY(connect(session_, SIGNAL(onTitleSet(const QString&)), this, SLOT(onTitleSet(const QString&))));

    FQ_VERIFY(connect(session_, SIGNAL(messageAutoReplied()), this, SLOT(messageAutoReplied())));
    FQ_VERIFY(connect(session_, SIGNAL(telnetStateChanged(int)),  this, SLOT(TelnetState(int))));
    FQ_VERIFY(connect(session_, SIGNAL(errorMessage(QString)),this, SLOT(showSessionErrorMessage(QString))));

    FQ_VERIFY(connect(tabBlinkTimer_, SIGNAL(timeout()), this, SLOT(blinkTab())));

    FQ_VERIFY(connect(externalEditor_, SIGNAL(done(const QString&)), this, SLOT(externalEditorDone(const QString&))));

    FQ_VERIFY(connect(this, SIGNAL(writeStringSignal(const QString&)), this, SLOT(writeString(const QString&)), Qt::QueuedConnection));
    FQ_VERIFY(connect(this, SIGNAL(writeRawStringSignal(const QString&)), this, SLOT(writeRawString(const QString&)), Qt::QueuedConnection));

    popWindow_ = new popWidget(this, frame_);

    const QString &resource_dir = getPath(RESOURCE);

    cursors_[FQTermSession::kHome] = QCursor( QPixmap(resource_dir + "cursor/home.xpm"));
    cursors_[FQTermSession::kEnd] = QCursor( QPixmap(resource_dir + "cursor/end.xpm"));
    cursors_[FQTermSession::kPageUp] = QCursor( QPixmap(resource_dir + "cursor/pageup.xpm"));
    cursors_[FQTermSession::kPageDown] = QCursor(QPixmap(resource_dir + "cursor/pagedown.xpm"));
    cursors_[FQTermSession::kUp] = QCursor(	QPixmap(resource_dir + "cursor/prev.xpm"));
    cursors_[FQTermSession::kDown] = QCursor(QPixmap(resource_dir + "cursor/next.xpm"));
    cursors_[FQTermSession::kLeft] = QCursor(QPixmap(resource_dir + "cursor/exit.xpm"), 0, 10);
    cursors_[FQTermSession::kRight] = QCursor(QPixmap(resource_dir + "cursor/hand.xpm"));
    cursors_[FQTermSession::kNormal] = Qt::ArrowCursor;

    script_engine_ = new FQTermScriptEngine(this);
    if  (session_->param().isAutoLoadScript_  && !session_->param().autoLoadedScriptFileName_.isEmpty()) {
        const QString &filename = session_->param().autoLoadedScriptFileName_;
        runScript(filename);
    }
    session_->setScriptListener(this);
}

FQTermWindow::~FQTermWindow()
{
    script_engine_->finalizeScript();
    //  delete telnet_;
    delete session_;
    delete popWindow_;
    delete tabBlinkTimer_;
    delete menu_;
    delete urlMenu_;
    delete screen_;
    delete pageViewMessage_;
    //delete script_engine_;
}

void FQTermWindow::addMenu() {
    urlMenu_ = new QMenu(screen_);
    urlMenu_->addAction(tr("Preview image"), this, SLOT(previewLink()));
    urlMenu_->addAction(tr("Open link"), this, SLOT(openLink()));
    urlMenu_->addAction(tr("Save As..."), this, SLOT(saveLink()));
    urlMenu_->addAction(tr("Copy link address"), this, SLOT(copyLink()));
    urlMenu_->addSeparator();
    urlMenu_->addAction(tr("Share Selected Text and URL!"), this, SLOT(shareIt()));
    const QString &resource_dir = getPath(RESOURCE);

    menu_ = new QMenu(screen_);

    QKeySequence copy_shortcut(tr("Ctrl+Insert"));
    QKeySequence paste_shortcut(tr("Shift+Insert"));

    menu_->addAction(QPixmap(resource_dir + "pic/copy.png"), tr("Copy"),
                     this, SLOT(copy()), copy_shortcut);
    menu_->addAction(QPixmap(resource_dir + "pic/paste.png"), tr("Paste"),
                     this, SLOT(paste()), paste_shortcut);

    //menu_->addAction(QPixmap(resource_dir+"pic/get_article_fulltext.png"), tr("Copy Article"), this, SLOT(copyArticle()));
    menu_->addSeparator();

    QMenu *fontMenu = new QMenu(menu_);
    fontMenu->setTitle(tr("Font"));
    fontMenu->setIcon(QPixmap(resource_dir + "pic/change_fonts.png"));
    for (int i = 0; i < 2; ++i) {
        QAction *act = fontMenu->addAction(FQTermParam::getLanguageName(bool(i)) + tr(" Font"),	this, SLOT(setFont()));
        act->setData(i);
    }
    menu_->addMenu(fontMenu);

    menu_->addAction(QPixmap(resource_dir + "pic/ansi_color.png"), tr("Color"), this, SLOT(setColor()));
    menu_->addSeparator();
    menu_->addAction(QPixmap(resource_dir + "pic/preferences.png"), tr("Setting"), this, SLOT(setting()));
    menu_->addSeparator();
    menu_->addAction(tr("Open Selected As Url"), this, SLOT(openAsUrl()));
    menu_->addAction(tr("Search Selected Text!"), this, SLOT(searchIt()));
    menu_->addAction(tr("Share Selected Text and URL!"), this, SLOT(shareIt()));
}

void FQTermWindow::recreateMenu()
{
    delete urlMenu_;
    delete menu_;
    addMenu();
}

//close event received
void FQTermWindow::closeEvent(QCloseEvent *clse)
{
    bool toClose = true;
    if (isConnected() && FQTermPref::getInstance()->openWarnOnClose_) {
	QMessageBox mb(tr("vTerminal"),
                       tr("Still connected, do you really want to exit?"),
                       QMessageBox::Warning, QMessageBox::Yes|QMessageBox::Default,
                       QMessageBox::No | QMessageBox::Escape, 0, this);
        if (mb.exec() != QMessageBox::Yes) {
            toClose = false;
        }
    }
    if (toClose) {
        session_->close();
    } else {
        clse->ignore();
        return;
    }
    //We no longer save setting on close from r1036.
    //saveSetting();
}

void FQTermWindow::blinkTab() {
    emit blinkTheTab(this, blinkStatus_);
    blinkStatus_ = !blinkStatus_;
}

/* ------------------------------------------------------------------------ */
/*                                                                         */
/*                           Mouse & Key                                    */
/*                                                                          */
/* ------------------------------------------------------------------------ */
void FQTermWindow::enterEvent(QEvent*){}

void FQTermWindow::leaveEvent(QEvent*) {
    // TODO: code below doesn't work
    // QPoint temp(0, 0);
    // session_->setSelect(temp, temp, session_->isRectangleCopy_);
    // QApplication::postEvent(screen_, new QPaintEvent(QRect(0, 0, 0, 0));
    // &paintEvent);
}

void FQTermWindow::changeEvent(QEvent *e)
{
    if (e->type() == QEvent::WindowStateChange) {
        QWindowStateChangeEvent *event = (QWindowStateChangeEvent*)(e);
        Qt::WindowStates oldState = event->oldState();
        if (!isMaximized()) {
            emit refreshOthers(this);
        }
        if ( (oldState & Qt::WindowMinimized && !isMinimized()) ||
             (!(oldState & Qt::WindowActive) && isActiveWindow())) {
            forcedRepaintScreen();
        }
        if (oldState & Qt::WindowMaximized || isMaximized()) {
            emit resizeSignal(this);
        }
    }
}

void FQTermWindow::resizeEvent(QResizeEvent *qResizeEvent)
{
    emit resizeSignal(this);
}

bool FQTermWindow::event(QEvent *qevent) {
    bool res = false;
    QKeyEvent *keyEvent;
    switch(qevent->type()) {
    case QEvent::KeyPress:
        keyEvent = (QKeyEvent *)(qevent);
        if (keyEvent->key() == Qt::Key_Tab ||
                keyEvent->key() == Qt::Key_Backtab) {
            // Key_Tab and Key_Backtab are special, if we don't process them here,
            // the default behavior is to move focus. see QWidget::event.
            keyPressEvent(keyEvent);
            keyEvent->accept();
            res = true;
        }
        break;
    default:
        break;
    }

    res = res || QMainWindow::event(qevent);

    if (qevent->type() == QEvent::HoverMove || qevent->type() == QEvent::MouseMove || qevent->type() == QEvent::Move) {
        if (res) {
            FQ_TRACE("wndEvent", 10) << "Accept event: " << qevent->type()
                                     << " " << getEventName(qevent->type()) << ".";
        } else {
            FQ_TRACE("wndEvent", 10) << "Ignore event: " << qevent->type()
                                     << " " << getEventName(qevent->type()) << ".";
        }
    } else {
        if (res) {
            FQ_TRACE("wndEvent", 9) << "Accept event: " << qevent->type()
                                    << " " << getEventName(qevent->type()) << ".";
        } else {
            FQ_TRACE("wndEvent", 9) << "Ignore event: " << qevent->type()
                                    << " " << getEventName(qevent->type()) << ".";
        }
    }
    return res;
}

void FQTermWindow::mouseDoubleClickEvent(QMouseEvent *mouseevent) {
    if (scriptMouseEvent(mouseevent))
        return;
}

void FQTermWindow::mousePressEvent(QMouseEvent *mouseevent) {
    if (scriptMouseEvent(mouseevent))
        return;
    // stop  the tab blinking
    stopBlink();

    // Left Button for selecting
    if (mouseevent->button() & Qt::LeftButton && !(mouseevent->modifiers())) {
        startSelecting(mouseevent->pos());
    }

    // Right Button
    if ((mouseevent->button() & Qt::RightButton)) {
        if (mouseevent->modifiers() & Qt::ControlModifier) {
            // on Url
            if (!session_->getUrl().isEmpty()) {
                previewLink();
            }
            return ;
        }

        bool additional_modifier = mouseevent->modifiers();

        if (!(additional_modifier)) {
            // on Url
            if (!session_->getUrl().isEmpty()) {
                urlMenu_->popup(mouseevent->globalPos());
            } else {
                menu_->popup(mouseevent->globalPos());
            }
            return ;
        }
    }
    // Middle Button for paste
    if (mouseevent->button() &Qt::MidButton && !(mouseevent->modifiers())) {
        if (isConnected()) {
            // on Url
            if (!session_->getUrl().isEmpty()) {
                previewLink();
            } else {
                pasteHelper(false);
            }
        }
        return ;
    }

    // If it is a click, there should be a press event and a release event.
    isMouseClicked_ = true;

    // xterm mouse event
    //session_->sendMouseState(0, me->button(), me->state(), me->pos());
}


void FQTermWindow::mouseMoveEvent(QMouseEvent *mouseevent) {
    bool mouseEventConsumed = scriptMouseEvent(mouseevent);
    QPoint position = mouseevent->pos();
    if (!mouseEventConsumed)
    {

        // selecting by leftbutton
        if ((mouseevent->buttons() &Qt::LeftButton) && isSelecting_) {
            onSelecting(position);
        }
    }
    if (!(session_->param().isSupportMouse_ && isConnected())) {
        return;
    }

    setCursorPosition(position);
    setCursorType(position);


    if (!mouseEventConsumed)
    {
        if (!isUrlUnderLined_ && session_->urlStartPoint() != session_->urlEndPoint()) {
            isUrlUnderLined_ = true;
            urlStartPoint_ = session_->urlStartPoint();
            urlEndPoint_ = session_->urlEndPoint();
            clientRect_ = QRect(QPoint(0, urlStartPoint_.y()), QSize(session_->getBuffer()->getNumColumns(), urlEndPoint_.y() - urlStartPoint_.y() + 1));
            repaintScreen();

        } else if (isUrlUnderLined_ && (session_->urlStartPoint() != urlStartPoint_ || session_->urlEndPoint() != urlEndPoint_)) {
            clientRect_ = QRect(QPoint(0, urlStartPoint_.y()), QSize(session_->getBuffer()->getNumColumns(), urlEndPoint_.y() - urlStartPoint_.y() + 1));
            urlStartPoint_ = QPoint();
            urlEndPoint_ = QPoint();
            repaintScreen();
            isUrlUnderLined_ = false;

        }
    }
}

static bool isSupportedImage(const QString &name)
{
    static QList<QByteArray> image_formats =
            QImageReader::supportedImageFormats();

    return image_formats.contains(name.section(".", -1).toLower().toUtf8());
}

void FQTermWindow::mouseReleaseEvent(QMouseEvent *mouseevent) {
    if (scriptMouseEvent(mouseevent))
        return;
    if (!isMouseClicked_) {
        return ;
    }
    isMouseClicked_ = false;
    // other than Left Button ignored
    if (!(mouseevent->button() & Qt::LeftButton) || (mouseevent->modifiers() & Qt::KeyboardModifierMask)) {
        // no local mouse event
        //session_->sendMouseState(3, me->button(), me->state(), me->pos());
        return ;
    }

    // Left Button for selecting
    QPoint currentMouseCell = screen_->mapToChar(mouseevent->pos());
    if (currentMouseCell != lastMouseCell_ && isSelecting_) {
        finishSelecting(mouseevent->pos());
        return ;
    }
    isSelecting_ = false;

    if (!session_->param().isSupportMouse_ || !isConnected()) {
        return ;
    }

    // url
    QString url = session_->getUrl();
    if (!url.isEmpty()) {
        if (isSupportedImage(url)) {
            previewLink();
        } else {
            openUrl(url);
        }
        return ;
    }

    processLClick(currentMouseCell);
}
/*
void FQTermWindow::wheelEvent(QWheelEvent *wheelevent) {
    if (scriptWheelEvent(wheelevent)) return;
    int j = wheelevent->delta() > 0 ? 4 : 5;
    if (!(wheelevent->modifiers())) {
        if (FQTermPref::getInstance()->isWheelSupported_ && isConnected()) {
            session_->writeStr(directions_[j]);
        }
        return ;
    }
    //session_->sendMouseState(j, Qt::NoButton, we->state(), we->pos());
}
*/
//keyboard input event
void FQTermWindow::keyPressEvent(QKeyEvent *keyevent)
{
    if (keyevent->isAutoRepeat() && !session_->getBuffer()->isAutoRepeatMode()) {
        FQ_TRACE("sendkey", 5) << "The terminal is set to not allow repeated key events.";
        keyevent->accept();
        return;
    }
    if (scriptKeyEvent(keyevent))
        return;

    keyevent->accept();

    if (!isConnected()) {
        if (keyevent->key() == Qt::Key_Return || keyevent->key() == Qt::Key_Enter) {
            session_->reconnect();
        } else if (keyevent->key() == Qt::Key_Space) {
            emit(connectionClosed(this));
        }
        return ;
    }

    // stop  the tab blinking
    stopBlink();
    if (!session_->readyForInput()) {
        return;
    }
    // message replying
    session_->leaveIdle();

    Qt::KeyboardModifiers modifier = QApplication::keyboardModifiers();
    int key = keyevent->key();
    sendKey(key, modifier, keyevent->text());
}
/*
  void FQTermWindow::focusInEvent (QFocusEvent *event) {
  QMainWindow::focusInEvent(event);
  screen_->setFocus(Qt::OtherFocusReason);
  }
*/

//connect slot
void FQTermWindow::connectHost()
{
    //	pageViewMessage_->display(tr("Not connected"), screen_->mapToPixel(QPoint(1, 1)));
    session_->setProxy(session_->param().proxyType_,
                       session_->param().isAuthentation_,
                       session_->param().proxyHostName_,
                       session_->param().proxyPort_,
                       session_->param().proxyUserName_,
                       session_->param().proxyPassword_);
    session_->connectHost(session_->param().hostAddress_, session_->param().port_);
    config_->setItemValue("global", "lastaddrindex", QString("%1").arg(addressIndex_));
}

bool FQTermWindow::isConnected()
{
    return session_->isConnected();
}

void FQTermWindow::sessionUpdated()
{
    refreshScreen();
    //send a mouse move event to make mouse-related change
    QMouseEvent* me = new QMouseEvent( QEvent::MouseMove, mapFromGlobal(QCursor::pos()), QCursor::pos(), Qt::NoButton, Qt::NoButton, Qt::NoModifier);
    QApplication::postEvent(this, me);
}

void FQTermWindow::requestUserPwd(QString *user, QString *pwd, bool *isOK)
{
    SSHLoginDialog login(user, pwd, this);
    *isOK = (login.exec() == QDialog::Accepted);
}

void FQTermWindow::warnInsecure(const QString &msg, bool *isOK)
{
    QMessageBox mb(QMessageBox::Warning, "vTerminal", msg,
                   QMessageBox::Yes | QMessageBox::No, this);
    *isOK = (mb.exec() == QMessageBox::Yes);
}

void FQTermWindow::ZmodemState(int type, int value, const char *status)
{
    QString strMsg;
    //to be completed
    switch (type) {
    case RcvByteCount:
    case SndByteCount:
        zmodemDialog_->setProgress(value);
        return;
    case RcvTimeout:
        /* receiver did not respond, aborting */
        strMsg = QString(tr("Time out!"));
        break;
    case SndTimeout:
        /* value is # of consecutive send timeouts */
        strMsg = QString(tr("Time out after trying %1 times")).arg(value);
        break;
    case RmtCancel:
        /* remote end has cancelled */
        strMsg = QString(tr("Canceled by remote peer %1")).arg(status);
        //zmodemDialog_->close();
        break;
    case ProtocolErr:
        /* protocol error has occurred, val=hdr */
        strMsg = QString( tr("Unhandled header %1 at state %2")).arg(value).arg(status);
        break;
    case RemoteMessage:
        /* message from remote end */
        strMsg = QString(tr("Msg from remote peer: %1")).arg(status);
        break;
    case DataErr:
        /* data error, val=error count */
        strMsg = QString(tr("Data errors %1")).arg(value);
        break;
    case FileErr:
        /* error writing file, val=errno */
        strMsg = QString(tr("Falied to write file"));
        break;
    case FileBegin: /* file transfer begins, str=name */
        zmodemDialog_->setFileInfo(session_->bbs2unicode(status), value);
        zmodemDialog_->setProgress(0);
        zmodemDialog_->clearErrorLog();
        zmodemDialog_->show();
        zmodemDialog_->setModal(true);
        return;
    case FileEnd:
        /* file transfer ends, str=name */
        zmodemDialog_->hide();
        return;
    case FileSkip:
        /* file being skipped, str=name */
        strMsg = QString(tr("Skipping file %1")).arg(status);
        break;
    }
    zmodemDialog_->addErrorLog(strMsg);
}

// telnet state slot
void FQTermWindow::TelnetState(int state) {
    switch (state) {
    case TSRESOLVING:
        //pageViewMessage_->display(tr("Resolving host name"), screen_->mapToPixel(QPoint(1, 1)));
        break;
    case TSHOSTFOUND:
        //pageViewMessage_->display(tr("Host found"), screen_->mapToPixel(QPoint(1, 1)));
        break;
    case TSHOSTNOTFOUND:
        pageViewMessage_->display(tr("Host not found"), screen_->mapToPixel(QPoint(1, 1)));
        break;
    case TSCONNECTING:
        pageViewMessage_->display(tr("Connecting..."), screen_->mapToPixel(QPoint(1, 1)));
        break;
    case TSHOSTCONNECTED:
        //pageViewMessage_->display(tr("Connected"), screen_->mapToPixel(QPoint(1, 1)));
        frame_->updateMenuToolBar();
        break;
    case TSPROXYCONNECTED:
        //pageViewMessage_->display(tr("Connected to proxy"), screen_->mapToPixel(QPoint(1, 1)));
        break;
    case TSPROXYAUTH:
        //pageViewMessage_->display(tr("Proxy authentation"), screen_->mapToPixel(QPoint(1, 1)));
        break;
    case TSPROXYFAIL:
        pageViewMessage_->display(tr("Proxy failed"), screen_->mapToPixel(QPoint(1, 1)));
        break;
    case TSREFUSED:
        pageViewMessage_->display(tr("Connection refused"), screen_->mapToPixel(QPoint(1, 1)));
        break;
    case TSREADERROR:
        pageViewMessage_->display(tr("Error when reading from server"), screen_->mapToPixel(QPoint(1, 1)), PageViewMessage::Error);
        break;
    case TSCLOSED:
        // pageViewMessage_->display(tr("Connection closed"), screen_->mapToPixel(QPoint(1, 1)));
        break;
    case TSCLOSEFINISH:
        //pageViewMessage_->display(tr("Connection close finished"), screen_->mapToPixel(QPoint(1, 1)));
        break;
    case TSCONNECTVIAPROXY:
        pageViewMessage_->display(tr("Connect to host via proxy"), screen_->mapToPixel(QPoint(1, 1)));
        break;
    case TSEGETHOSTBYNAME:
        pageViewMessage_->display( tr("Error in gethostbyname"), screen_->mapToPixel(QPoint(1, 1)), PageViewMessage::Error);
        break;
    case TSEINIWINSOCK:
        pageViewMessage_->display( tr("Error in startup winsock"), screen_->mapToPixel(QPoint(1, 1)), PageViewMessage::Error);
        break;
    case TSERROR:
        pageViewMessage_->display( tr("Error in connection"), screen_->mapToPixel(QPoint(1, 1)), PageViewMessage::Error);
        break;
    case TSPROXYERROR:
        pageViewMessage_->display(tr("Error in proxy"), screen_->mapToPixel(QPoint(1, 1)), PageViewMessage::Error);
        break;
    case TSWRITED:
        break;
    }
}

void FQTermWindow::showSessionErrorMessage(QString reason)
{
    if (reason == tr("User Cancel")) {
        return;
    }
    QMessageBox::critical(this, tr("Session error"), reason);
}

/* ------------------------------------------------------------------------ */
/*                                                                         */
/*                           UI Slots                                       */
/*                                                                          */
/* ------------------------------------------------------------------------ */


void FQTermWindow::copy() {
    QClipboard *clipboard = QApplication::clipboard();

    QString selected_text = session_->getBuffer()->getTextSelected(
                session_->param().isRectSelect_,
                session_->param().isColorCopy_,
                parseString((const char*)session_->param().escapeString_.toLatin1()));

    QByteArray cstrText = session_->unicode2bbs(selected_text);

    // TODO_UTF16: avoid this replacement.
    if (!FQTermPref::getInstance()->escapeString_.isEmpty()) {
        cstrText.replace(
                    parseString((const char*)session_->param().escapeString_.toLatin1()),
                    parseString(FQTermPref::getInstance()->escapeString_.toLatin1()));
    }
    selected_text = session_->bbs2unicode(cstrText);

    // TODO_UTF16: there are three modes: Clipboard, Selection, FindBuffer.
    clipboard->setText(selected_text, QClipboard::Clipboard);
    clipboard->setText(selected_text, QClipboard::Selection);
}

void FQTermWindow::paste() {
    pasteHelper(true);
}

void FQTermWindow::writePasting(const QString& content)
{
    QByteArray cstrText = session_->unicode2bbs(content);

    // TODO_UTF16: avoid this replacement.
    if (!FQTermPref::getInstance()->escapeString_.isEmpty()) {
        cstrText.replace(
                    parseString(FQTermPref::getInstance()->escapeString_.toLatin1()),
                    parseString((const char*)session_->param().escapeString_.toLatin1()));
    }

    if (session_->param().isAutoWrap_) {
        // convert to unicode for word wrap
        QString strText;
        strText = session_->bbs2unicode(cstrText);
        // insert '\n' as needed
        for (uint i = 0; (long)i < strText.length(); i++) {
            uint j = i;
            uint k = 0, l = 0;
            while ((long)j < strText.length() && strText.at(j) != QChar('\n')) {
                if (FQTermPref::getInstance()->widthToWrapWord_ - (l - k) >= 0
                        && FQTermPref::getInstance()->widthToWrapWord_ - (l - k) < 2) {
                    strText.insert(j, QChar('\n'));
                    k = l;
                    j++;
                    break;
                }
                // double byte or not
                if (strText.at(j).row() == '\0') {
                    l++;
                } else {
                    l += 2;
                }
                j++;
            }
            i = j;
        }

        cstrText = session_->unicode2bbs(strText);
    }

    session_->write(cstrText, cstrText.length());
}

void FQTermWindow::pasteHelper(bool clip) {
    if (!isConnected()) {
        return ;
    }

    // TODO_UTF16: there are three modes: Clipboard, Selection, FindBuffer.
    QClipboard *clipboard = QApplication::clipboard();

    // TODO_UTF16: what's the termFrame_->clipboardEncodingID_?
    /*
      if (termFrame_->clipboardEncodingID_ == 0) {
      if (clip) {
      cstrText = U2G(clipboard->text(QClipboard::Clipboard));
      }
      else {
      cstrText = U2G(clipboard->text(QClipboard::Selection));
      }

      if (session_->param().serverEncodingID_ == 1) {
      char *str = encodingConverter_.G2B(cstrText, cstrText.length());
      cstrText = str;
      delete [] str;
      }
      } else {
      if (clip) {
      cstrText = U2B(clipboard->text(QClipboard::Clipboard));
      } else {
      cstrText = U2B(clipboard->text(QClipboard::Selection));
      }

      if (session_->param().serverEncodingID_ == 0) {
      char *str = encodingConverter_.B2G(cstrText, cstrText.length());
      cstrText = str;
      delete [] str;
      }
      }
    */

    QString clipStr;

    if (clip) {
        clipStr = clipboard->text(QClipboard::Clipboard);
    } else {
        clipStr = clipboard->text(QClipboard::Selection);
    }

    writePasting(clipStr);
}

void FQTermWindow::openAsUrl() {
    QString selected_text = session_->getBuffer()->getTextSelected(
                session_->param().isRectSelect_,
                session_->param().isColorCopy_,
                parseString((const char*)session_->param().escapeString_.toLatin1()));
    openUrl(selected_text);
}

void FQTermWindow::searchIt()
{
    script_engine_->runScript(getPath(RESOURCE) + "script/search.js");
    QScriptValueList qvl;
    qvl << FQTermPref::getInstance()->searchEngine_;
    if (!script_engine_->scriptCallback("searchSelected", qvl)) {
        //fall back to google.
        QString selected_text = session_->getBuffer()->getTextSelected(
                    session_->param().isRectSelect_,
                    session_->param().isColorCopy_,
                    parseString((const char*)session_->param().escapeString_.toLatin1()));
        QString searchUrl = "http://www.baidu.com/search?client=fqterm&rls=en&q=" + selected_text + "&sourceid=fqterm";
        QByteArray url = QUrl(searchUrl).toEncoded();
        openUrlImpl(url);
    }
}

void FQTermWindow::shareIt()
{
    script_engine_->runScript(getPath(RESOURCE) + "script/weiboshare.js");
}

void FQTermWindow::fastPost()
{
    QClipboard *clipboard = QApplication::clipboard();
    QString clipStr = clipboard->text(QClipboard::Clipboard);
    QString lineEnd("\n");
    QString postTitle = clipStr.left(clipStr.indexOf(lineEnd));

    sendParsedString("^p");
    writeRawString(postTitle);
    writeRawString(QString("\n\n"));
    writePasting(clipStr);
    sendParsedString("^W\n");
}


void FQTermWindow::externalEditor() {
    externalEditor_->start();
}


void FQTermWindow::externalEditorDone(const QString& str) {
    QByteArray rawStr = session_->unicode2bbs_smart(str);
    session_->write(rawStr, rawStr.length());
}


void FQTermWindow::copyArticle() {
    if (!isConnected()) {
        return ;
    }

    session_->copyArticle();
}

void FQTermWindow::setColor() {
    addrDialog set(this, session_->param());

    set.setCurrentTabIndex(addrDialog::Display);
    int res = set.exec();
    if (res != 0) {
        updateSetting(set.param());
        if (res == 2)
            saveSetting(false);
    }

}

void FQTermWindow::disconnect() {
    session_->close();

    connectionClosed();
}

void FQTermWindow::showIP(bool show) {
    pageViewMessage_->hide();
    if (!show) {
        return;
    }
    QString country, city;
    QString url = session_->getIP();
    FQTermIPLocation *ipLocation = FQTermIPLocation::getInstance();

    QRect screenRect = screen_->rect();
    QPoint messagePos;
    QRect globalIPRectangle = QRect(screen_->mapToRect(ipRectangle_));
    QFontMetrics fm(qApp->font());
    int charHeight = fm.height();

    int midLine = (screenRect.top() + screenRect.bottom()) / 2;
    int ipMidLine = (globalIPRectangle.top() + globalIPRectangle.bottom()) / 2;
    if (ipMidLine < midLine) {
        // "There is Plenty of Room at the Bottom." -- Feyman, 1959
        messagePos.setY(globalIPRectangle.bottom() + 0.618 * charHeight);
    } else {
        messagePos.setY(globalIPRectangle.top()
                        - 0.618 * charHeight
                        - pageViewMessage_->size().height());
    }

    QString displayText;
    if (ipLocation == NULL) {
        displayText = tr("IP database file does NOT exist");
    } else if (!ipLocation->getLocation(url, country, city)) {
        displayText = tr("Invalid IP");
    } else {
        displayText = country + " " + city;
    }

    QRect messageSize(pageViewMessage_->displayCheck(displayText,
                                                     PageViewMessage::Info));
    PageViewMessage::Alignment ali;
    if (messageSize.width() + globalIPRectangle.left() >= screenRect.right()) {
        //"But There is No Room at the Right" -- Curvelet, 2007
        messagePos.setX(globalIPRectangle.right());
        ali = PageViewMessage::TopRight;
    } else {
        messagePos.setX(globalIPRectangle.left());
        ali = PageViewMessage::TopLeft;
    }

    pageViewMessage_->display(
                displayText,
                PageViewMessage::Info,
                0, messagePos, ali);
}

void FQTermWindow::runScript() {
    // get the previous dir
    QStringList fileList;
    FQTermFileDialog fileDialog(config_);

    fileList = fileDialog.getOpenNames("Choose a Javascript file", "JavaScript File (*.js)");

    if (!fileList.isEmpty() && fileList.count() == 1) {
        runScript(fileList.at(0));
    }
}

void FQTermWindow::stopScript() {
    script_engine_->stopScript();
}

void FQTermWindow::viewMessages() {
    msgDialog msg(this);

    QByteArray dlgSize =
            //      frame_->config()->getItemValue("global", "msgdialog").toLatin1();
            config_->getItemValue("global", "msgdialog").toLatin1();
    const char *dsize = dlgSize.constData();
    if (!dlgSize.isEmpty()) {
        int x, y, cx, cy;
        sscanf(dsize, "%d %d %d %d", &x, &y, &cx, &cy);
        msg.resize(QSize(cx, cy));
        msg.move(QPoint(x, y));
    } else {
        msg.resize(QSize(300, 500));
        msg.move(QPoint(20,20));
    }

    msg.setMessageText(allMessages_);
    msg.exec();

    QString strSize = QString("%1 %2 %3 %4").arg(msg.x()).arg(msg.y()).arg
            (msg.width()).arg(msg.height());
    //  frame_->config()->setItemValue("global", "msgdialog", strSize);
    config_->setItemValue("global", "msgdialog", strSize);
}

void FQTermWindow::setting() {
    addrDialog set(this, session_->param());
    int res = set.exec();
    if (res != 0) {
        updateSetting(set.param());
        if (res == 2)
            saveSetting(false);
    }
}

void FQTermWindow::toggleAntiIdle() {
    session_->setAntiIdle(!session_->isAntiIdle());
}

void FQTermWindow::toggleAutoReply() {
    session_->setAutoReply(!session_->isAutoReply());
}

void FQTermWindow::toggleAutoReconnect() {
    session_->setAutoReconnect(!session_->param().isAutoReconnect_);
}

void FQTermWindow::connectionClosed() {
    stopBlink();

    //pageViewMessage_->display(tr("Connection closed"), screen_->mapToPixel(QPoint(1, 1)));

    frame_->updateMenuToolBar();

    setCursor(cursors_[FQTermSession::kNormal]);

    refreshScreen();

    if (!getSession()->param().isAutoReconnect_ && getSession()->param().isAutoCloseWin_) {
        emit(connectionClosed(this));
    }
}


void FQTermWindow::articleCopied(int e, const QString content) {
    if (e == DAE_FINISH) {
        //    articleDialog article(frame_->config(), this);
        articleDialog article(config_, this);

        // Fix focus-losing bug.
        // dsize should be a pointer to a non-temprary object.
        QByteArray dlgSize =
                //        frame_->config()->getItemValue("global", "articledialog").toLatin1();
                config_->getItemValue("global", "articledialog").toLatin1();

        const char *dsize = dlgSize.constData();

        if (!dlgSize.isEmpty()) {
            int x, y, cx, cy;
            sscanf(dsize, "%d %d %d %d", &x, &y, &cx, &cy);
            article.resize(QSize(cx, cy));
            article.move(QPoint(x, y));
        } else {
            article.resize(QSize(300, 500));
            article.move(20,20);
        }
        article.articleText_ = content;

        article.ui_.textBrowser->setPlainText(article.articleText_);
        article.exec();
        QString strSize = QString("%1 %2 %3 %4").arg(article.x()).arg(article.y())
                .arg(article.width()).arg(article.height());
        //    frame_->config()->setItemValue("global", "articledialog", strSize);
        config_->setItemValue("global", "articledialog", strSize);
    } else if (e == DAE_TIMEOUT) {
        QMessageBox::warning(this, "timeout", "download article timeout, aborted");
#ifdef HAVE_PYTHON
    } else if (e == PYE_ERROR) {
        QMessageBox::warning(this, "Python script error", pythonErrorMessage_);
    } else if (e == PYE_FINISH) {
        QMessageBox::information(this, "Python script finished",
                                 "Python script file executed successfully");
#endif //HAVE_PYTHON          
    }
}

/* ------------------------------------------------------------------------ */
/*                                                                         */
/*                           Aux Func                                       */
/*                                                                          */
/* ------------------------------------------------------------------------ */

QByteArray FQTermWindow::parseString(const QByteArray &cstr, int *len) {
    QByteArray parsed = "";

    if (len != 0) {
        *len = 0;
    }

    for (uint i = 0; (long)i < cstr.length(); i++) {
        if (cstr.at(i) == '^') {
            i++;
            if ((long)i < cstr.length()) {
                parsed += FQTERM_CTRL(cstr.at(i));
                if (len != 0) {
                    *len =  *len + 1;
                }
            }
        } else if (cstr.at(i) == '\\') {
            i++;
            if ((long)i < cstr.length()) {
                if (cstr.at(i) == 'n') {
                    parsed += CHAR_CR;
                } else if (cstr.at(i) == 'r') {
                    parsed += CHAR_LF;
                } else if (cstr.at(i) == 't') {
                    parsed += CHAR_TAB;
                }
                if (len != 0) {
                    *len =  *len + 1;
                }
            }
        } else {
            parsed += cstr.at(i);
            if (len != 0) {
                *len =  *len + 1;
            }
        }
    }

    return parsed;
}

void FQTermWindow::messageAutoReplied()
{
    pageViewMessage_->display("You have messages", PageViewMessage::Info, 0);
}

void FQTermWindow::saveSetting(bool ask /* = true*/) {
    if (addressIndex_ == -1) {
        return ;
    }

    //save these options silently
    FQTermConfig pConf(getPath(USER_CONFIG) + "address.cfg");
    FQTermParam param;
    loadAddress(&pConf, addressIndex_, param);
    param.isAutoCopy_ = session_->param().isAutoCopy_;
    param.isAutoWrap_ = session_->param().isAutoWrap_;
    param.isRectSelect_ = session_->param().isRectSelect_;
    param.isColorCopy_ = session_->param().isColorCopy_;
    saveAddress(&pConf, addressIndex_, param);
    pConf.save(getPath(USER_CONFIG) + "address.cfg");

    if (param == session_->param()) {
        return;
    }

    QMessageBox mb("vTerminal", "Setting changed do you want to save it?",
                   QMessageBox::Warning, QMessageBox::Yes | QMessageBox::Default,
                   QMessageBox::No | QMessageBox::Escape, 0, this);
    if (!ask || mb.exec() == QMessageBox::Yes) {
        saveAddress(&pConf, addressIndex_, session_->param());
        pConf.save(getPath(USER_CONFIG) + "address.cfg");
    }
}

int FQTermWindow::externInput(const QByteArray &cstrText)
{
    QByteArray cstrParsed = parseString(cstrText);
    return session_->write(cstrParsed, cstrParsed.length());
}

int FQTermWindow::externInput(const QString &cstrText) {
    return externInput(session_->unicode2bbs_smart(cstrText));
}

int FQTermWindow::writeRawString(const QString& str)
{
    QByteArray rawStr = session_->unicode2bbs_smart(str);
    return session_->write(rawStr, rawStr.length());
}

void FQTermWindow::sendParsedString(const char *str) {
    int length;
    QByteArray cstr = parseString(str, &length);
    session_->write(cstr, length);
}


bool FQTermWindow::postQtScriptCallback(const QString& func, const QScriptValueList & args) {
    return script_engine_->scriptCallback(func, args);
}

#ifdef HAVE_PYTHON
bool FQTermWindow::postPythonCallback( const QString& func, PyObject* pArgs )
{
    return pythonCallback(func, pArgs);
}
#endif //HAVE_PYTHON

// void FQTermWindow::setMouseMode(bool on) {
//   isMouseX11_ = on;
// }

//  /* 0-left 1-middle 2-right 3-relsase 4/5-wheel
//   *
//   */
//  //void FQTermWindow::sendMouseState( int num,
//  //    Qt::ButtonState btnstate, Qt::ButtonState keystate, const QPoint& pt )
//  void FQTermWindow::sendMouseState(int num, Qt::KeyboardModifier btnstate,
//                           Qt::KeyboardModifier keystate, const QPoint &pt) {
//    /*
//      if(!m_bMouseX11)
//      return;
//
//      QPoint ptc = screen_->mapToChar(pt);
//
//      if(btnstate&Qt::LeftButton)
//      num = num==0?0:num;
//      else if(btnstate&Qt::MidButton)
//      num = num==0?1:num;
//      else if(btnstate&Qt::RightButton)
//      num = num==0?2:num;
//
//      int state = 0;
//      if(keystate&Qt::ShiftModifier)
//      state |= 0x04;
//      if(keystate&Qt::MetaModifier)
//      state |= 0x08;
//      if(keystate&Qt::ControlModifier)
//      state |= 0x10;
//
//      // normal buttons are passed as 0x20 + button,
//      // mouse wheel (buttons 4,5) as 0x5c + button
//      if(num>3) num += 0x3c;
//
//      char mouse[10];
//      sprintf(mouse, "\033[M%c%c%c",
//      num+state+0x20,
//      ptc.x()+1+0x20,
//      ptc.y()+1+0x20);
//      m_pTelnet->write( mouse, strlen(mouse) );
//    */
//  }

/* ------------------------------------------------------------------------ */
/*                                                                         */
/*                           Python Func                                    */
/*                                                                          */
/* ------------------------------------------------------------------------ */
#ifdef HAVE_PYTHON


void FQTermWindow::runPythonScript() {
    QStringList fileList;
    FQTermFileDialog fileDialog(config_);

    fileList = fileDialog.getOpenNames("Choose a Python file", "Python File (*.py)");

    if (!fileList.isEmpty() && fileList.count() == 1) {
        runPythonScriptFile(fileList.at(0));
    }
}

void FQTermWindow::runPythonScriptFile(const QString& file) {
    QString str(QString("%1").arg(long(this)));
    char* lp = fq_strdup(str.toUtf8().data());
    char *argv[2]={lp,NULL};
    // get the global python thread lock
    PyEval_AcquireLock();
    
    PyInterpreterState * mainInterpreterState = frame_->getPythonHelper()->getPyThreadState()->interp;

    // create a thread state object for this thread
    PyThreadState * myThreadState = PyThreadState_New(mainInterpreterState);
    PyThreadState_Swap(myThreadState);


    PySys_SetArgv(1, argv);

    runPythonFile(file);

    //Clean up this thread's python interpreter reference
    PyThreadState_Swap(NULL);
    PyThreadState_Clear(myThreadState);
    PyThreadState_Delete(myThreadState);
    PyEval_ReleaseLock();
    free((void*)lp);
}

bool FQTermWindow::pythonCallback(const QString & func, PyObject* pArgs) {
    if(!pythonScriptLoaded_) {
        Py_DECREF(pArgs);
        return false;
    };

    bool done = false;
    // get the global lock
    PyEval_AcquireLock();
    // get a reference to the PyInterpreterState

    //Python thread references

    PyInterpreterState * mainInterpreterState = frame_->getPythonHelper()->getPyThreadState()->interp;
    // create a thread state object for this thread
    PyThreadState * myThreadState = PyThreadState_New(mainInterpreterState);
    PyThreadState_Swap(myThreadState);
    
    PyObject *pF = PyString_FromString(func.toUtf8());
    PyObject *pFunc = PyDict_GetItem(pDict, pF);
    Py_DECREF(pF);

    if (pFunc && PyCallable_Check(pFunc))
    {
        PyObject *pValue = PyObject_CallObject(pFunc, pArgs);
        Py_DECREF(pArgs);
        if (pValue != NULL)
        {
            if (PyBool_Check(pValue) && pValue == Py_True)
                done = true;
            Py_DECREF(pValue);
        }
        else
        {
            //QMessageBox::warning(this,"Python script error", getException());
            printf("%p: Python script error\n", this, getException().toUtf8().data());
        }
    }
    else
    {
        PyErr_Print();
        printf("Cannot find python %s callback function\n", func.toUtf8().data());
    }

    // swap my thread state out of the interpreter
    PyThreadState_Swap(NULL);
    // clear out any cruft from thread state object
    PyThreadState_Clear(myThreadState);
    // delete my thread state object
    PyThreadState_Delete(myThreadState);
    // release the lock
    PyEval_ReleaseLock();

    if (func == "autoReply")
        startBlink();

    return done;
}


int FQTermWindow::runPythonFile( const QString& file )
{
    QString buffer("def work_thread():\n"
                   "\ttry:\n\t\texecfile('");
    buffer += QString(file).replace("\\", "\\\\");

    /* Have to do it like this. PyRun_SimpleFile requires you to pass a
     * stdio file pointer, but Vim and the Python DLL are compiled with
     * different options under Windows, meaning that stdio pointers aren't
     * compatible between the two. Yuk.
     *
     * Put the string "execfile('file')" into buffer. But, we need to
     * escape any backslashes or single quotes in the file name, so that
     * Python won't mangle the file name.
     * ---- kafa
     */

    /* Put in the terminating "')" and a null */
    buffer += "',{})\n\0";

    buffer += QString("\texcept:\n"
                      "\t\texc, val, tb = sys.exc_info()\n"
                      "\t\tlines = traceback.format_exception(exc, val, tb)\n"
                      "\t\terr = string.join(lines)\n"
                      "\t\tprint err\n"
                      "\t\tf=open('%1','w')\n"
                      "\t\tf.write(err)\n"
                      "\t\tf.close()\n").arg(getErrOutputFile(this));

    buffer += QString("\t\tfqterm.formatError(%1)\n").arg((long)this);
    buffer += "\t\texit\n";

    /* Execute the file */
    PyRun_SimpleString("import thread,string,sys,traceback,fqterm");
    PyRun_SimpleString(buffer.toUtf8());
    PyRun_SimpleString(	"thread.start_new_thread(work_thread,())\n");

    return 0;
}

void FQTermWindow::initPython(const QString& file) {

    // get the global python thread lock
    PyEval_AcquireLock();

    // get a reference to the PyInterpreterState
    PyInterpreterState * mainInterpreterState = frame_->getPythonHelper()->getPyThreadState()->interp;



    // create a thread state object for this thread
    PyThreadState * myThreadState = PyThreadState_New(mainInterpreterState);
    PyThreadState_Swap(myThreadState);

    Py_InitModule4("fqterm", fqterm_methods,
                   NULL,(PyObject*)NULL,PYTHON_API_VERSION);
    
    PyImport_AddModule("fqterm");

    if(!file.isEmpty() )
    {
        QFileInfo info(file);
        PyRun_SimpleString("import sys");
        PyRun_SimpleString(QString("sys.path.append(\"%1/\")").arg(info.absolutePath()).toUtf8().data());
        PyObject *pName = PyString_FromString(info.baseName().toUtf8().data());
        pModule = PyImport_Import(pName);
        //PyErr_Print();
        Py_DECREF(pName);
        if (pModule != NULL)
            pDict = PyModule_GetDict(pModule);
        else
        {
            //printf("Failed to PyImport_Import\n");
        }

        if(pDict != NULL )
            pythonScriptLoaded_ = true;
        else
        {
            //printf("Failed to PyModule_GetDict\n");
        }
    }

    //Clean up this thread's python interpreter reference
    PyThreadState_Swap(NULL);
    PyThreadState_Clear(myThreadState);
    PyThreadState_Delete(myThreadState);
    PyEval_ReleaseLock();
    
}

void FQTermWindow::finalizePython()
{
    // get the global python thread lock
    PyEval_AcquireLock();

    // get a reference to the PyInterpreterState
    PyInterpreterState * mainInterpreterState = frame_->getPythonHelper()->getPyThreadState()->interp;

    // create a thread state object for this thread
    PyThreadState * myThreadState = PyThreadState_New(mainInterpreterState);
    PyThreadState_Swap(myThreadState);

    //Displose of current python module so we can reload in constructor.
    if(pModule!=NULL)
        Py_DECREF(pModule);

    //Clean up this thread's python interpreter reference
    PyThreadState_Swap(NULL);
    PyThreadState_Clear(myThreadState);
    PyThreadState_Delete(myThreadState);
    PyEval_ReleaseLock();
}

#endif //HAVE_PYTHON

/* ------------------------------------------------------------------------ */
/*                                                                         */
/*                           HTTP Func                                      */
/*                                                                          */
/* ------------------------------------------------------------------------ */

void FQTermWindow::openLink() {
    QString url = session_->getUrl();
    if (!url.isEmpty()) {
        openUrlImpl(url);
    }
}

void FQTermWindow::saveLink() {
    QString url = session_->getUrl();
    if (!url.isEmpty())
        getHttpHelper(url, false);
}

void FQTermWindow::openUrl(QString url)
{
    QString caption = tr("URL Dialog");
    QString strUrl = url;
    QTextEdit *textEdit = new QTextEdit();
    textEdit->setPlainText(strUrl);
    //textEdit->setFocus();
    textEdit->setTabChangesFocus(true);
    textEdit->moveCursor(QTextCursor::End);


    QPushButton *okButton = new QPushButton(tr("&Open URL"));
    QPushButton *cancelButton = new QPushButton(tr("&Cancel"));
    //  okButton->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
    //  cancelButton->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));


    QGridLayout *layout = new QGridLayout;
    layout->addWidget(textEdit, 0, 0, 1, -1);
    layout->addWidget(okButton, 1, 3);
    layout->addWidget(cancelButton, 1, 4);

    QDialog dlg(this);

    QFontMetrics fm(qApp->font());


    connect(okButton, SIGNAL(pressed()), &dlg, SLOT(accept()));
    connect(cancelButton, SIGNAL(pressed()), &dlg, SLOT(reject()));


    dlg.setWindowTitle(caption);
    dlg.setLayout(layout);
    dlg.resize(fm.width(QString(30, 'W')), fm.height() * (fm.width(strUrl) / fm.width(QString(30, 'W')) + 5));
    textEdit->clearFocus();
    okButton->setFocus(Qt::TabFocusReason);
    if (dlg.exec()) {
        openUrlImpl(textEdit->toPlainText());
    }
}


void FQTermWindow::openUrlImpl(QString url)
{
    const QString &httpBrowser = FQTermPref::getInstance()->httpBrowser_;
    if (httpBrowser.isNull() || httpBrowser.isEmpty()) {
#if QT_VERSION >= 0x040400
        QDesktopServices::openUrl(QUrl::fromEncoded(url.toLocal8Bit()));
#else
        QDesktopServices::openUrl(url);
#endif
    } else {
        runProgram(httpBrowser, url);
    }
}


void FQTermWindow::previewLink() {
    getHttpHelper(session_->getUrl(), true);
}

void FQTermWindow::copyLink() {
    QString strUrl;
    strUrl = session_->bbs2unicode(session_->getUrl().toLatin1());

    QClipboard *clipboard = QApplication::clipboard();

    clipboard->setText(strUrl, QClipboard::Selection);
    clipboard->setText(strUrl, QClipboard::Clipboard);
}

void FQTermWindow::getHttpHelper(const QString &url, bool preview) {

    const QString &strPool = FQTermPref::getInstance()->poolDir_;

    FQTermHttp *http = new FQTermHttp(config_, this, strPool, session_->param().serverEncodingID_);
    if (getSession()->param().proxyType_ != 0) {
        QString host = getSession()->param().proxyHostName_;
        int port = getSession()->param().proxyPort_;
        bool auth = getSession()->param().isAuthentation_;
        QString user = auth ? getSession()->param().proxyUserName_ : QString();
        QString pass = auth ? getSession()->param().proxyPassword_ : QString();
        switch (getSession()->param().proxyType_)
        {
        case 1:
        case 2:
            //no support in qt.
            FQ_TRACE("network", 0) << "proxy type not supported by qt, download will not use proxy.";
            break;
        case 3:
            http->setProxy(QNetworkProxy(QNetworkProxy::Socks5Proxy, host, port, user, pass));
            break;
        case 4:
            http->setProxy(QNetworkProxy(QNetworkProxy::HttpProxy, host, port, user, pass));
            break;
        }
    }
    FQTerm::StatusBar::instance()->newProgressOperation(http).setDescription(tr("Waiting header...")).setAbortSlot(http, SLOT(cancel())).setMaximum(100);
    FQTerm::StatusBar::instance()->resetMainText();
    FQTerm::StatusBar::instance()->setProgress(http, 0);

    FQ_VERIFY(connect(http, SIGNAL(headerReceived(FQTermHttp *, const QString &)),
                      this, SLOT(startHttpDownload(FQTermHttp*,const QString&))));

    FQ_VERIFY(connect(http,SIGNAL(done(QObject*)),this,SLOT(httpDone(QObject*))));

    FQ_VERIFY(connect(http, SIGNAL(message(const QString &)),
                      pageViewMessage_, SLOT(showText(const QString &))));

    FQ_VERIFY(connect(http, SIGNAL(done(QObject*)),
                      FQTerm::StatusBar::instance(),
                      SLOT(endProgressOperation(QObject*))));

    FQ_VERIFY(connect(http, SIGNAL(percent(int)),
                      FQTerm::StatusBar::instance(), SLOT(setProgress(int))));

    FQ_VERIFY(connect(http, SIGNAL(previewImage(const QString &, bool, bool)),
                      this, SLOT(httpPreviewImage(const QString &, bool, bool))));


    http->getLink(url, preview);
}

void FQTermWindow::startHttpDownload(
        FQTermHttp *pHttp, const QString &filedesp) {
    FQTerm::StatusBar::instance()->newProgressOperation(pHttp).setDescription
            (filedesp).setAbortSlot(pHttp, SLOT(cancel())).setMaximum(100);

    FQTerm::StatusBar::instance()->resetMainText();
}

void FQTermWindow::httpDone(QObject *pHttp) {
    pHttp->deleteLater();
}

void FQTermWindow::httpPreviewImage(const QString &filename, bool raiseViewer, bool done) 
{
    if (config_->getItemValue("preference", "image").isEmpty() || done) {
        previewImage(filename, raiseViewer);
    }
}

void FQTermWindow::previewImage(const QString &filename, bool raiseViewer) {
    //  QString strViewer = frame_->config()->getItemValue("preference", "image");
    QString strViewer = config_->getItemValue("preference", "image");

    if (strViewer.isEmpty()) {
        frame_->viewImages(filename, raiseViewer);
    } else if(raiseViewer) {
        runProgram(strViewer, filename);
    }

}

void FQTermWindow::beep() {
    if (session_->param().isBeep_) {
        if (session_->param().isBuzz_) {
            frame_->buzz(this);
        }
        if (FQTermPref::getInstance()->beepSoundFileName_.isEmpty() ||
                FQTermPref::getInstance()->openBeep_ == 3) {
            qApp->beep();
        }
        else {
            sound_ = NULL;

            switch (FQTermPref::getInstance()->beepMethodID_) {
            case 0:
                sound_ =
                        new FQTermSystemSound(FQTermPref::getInstance()->beepSoundFileName_);
                break;
            case 1:
                sound_ =
                        new FQTermExternalSound(FQTermPref::getInstance()->beepPlayerName_,
                                                FQTermPref::getInstance()->beepSoundFileName_);
                break;
            }

            if (sound_) {
                sound_->start();
            }
        }
    }

    QString strMsg = session_->getMessage();
    if (!strMsg.isEmpty()) {
        allMessages_ += strMsg + "\n\n";
    }

    session_->setSendingMessage();
}


void FQTermWindow::onTitleSet(const QString& title) {
    setWindowTitle(title);
}


void FQTermWindow::startBlink() {
    if (FQTermPref::getInstance()->openTabBlinking_) {
        if (!tabBlinkTimer_->isActive()) {
            tabBlinkTimer_->start(500);
        }
    }
}

void FQTermWindow::stopBlink() {
    if (tabBlinkTimer_->isActive()) {
        tabBlinkTimer_->stop();
        emit blinkTheTab(this, true);
    }
}

void FQTermWindow::setCursorType(const QPoint& mousePosition) {
    QRect rcOld;
    bool isUrl = false;
    if (FQTermPref::getInstance()->openUrlCheck_) {
        showIP(session_->isIP(ipRectangle_, rcOld));
        if (session_->isUrl(urlRectangle_, rcOld)) {
            setCursor(Qt::PointingHandCursor);
            isUrl = true;
        }
    }
    if (!isUrl) {
        FQTermSession::CursorType cursorType =
                session_->getCursorType(screen_->mapToChar(mousePosition));
        setCursor(cursors_[cursorType]);
    }
}

void FQTermWindow::setCursorPosition(const QPoint& mousePosition) {
    // set cursor pos, repaint if state changed
    QRect rc;
    if (session_->setCursorPos(screen_->mapToChar(mousePosition), rc)) {
        screen_->repaint(screen_->mapToRect(rc));

        //re-check whether the cursor is still on the selected rectangle -- dp
        QPoint localPos = mapFromGlobal(QCursor::pos()); //local.
        if (!session_->getMenuRect().contains(screen_->mapToChar(localPos)))
        {
            QRect dummyRect;
            rc = session_->getMenuRect();
            session_->setCursorPos(screen_->mapToChar(localPos), dummyRect);
            screen_->repaint(screen_->mapToRect(rc));
            //restore the state.
            session_->setCursorPos(screen_->mapToChar(mousePosition), dummyRect);
        }
    }
}

void FQTermWindow::refreshScreen() {
    screen_->setPaintState(FQTermScreen::NewData);
    screen_->update();
}

void FQTermWindow::repaintScreen() {

    screen_->setPaintState(FQTermScreen::Repaint);
    if (clientRect_ != QRect()) {
        screen_->repaint(screen_->mapToRect(clientRect_));
    } else {
        screen_->repaint();
    }
    clientRect_ = QRect();
    //  screen_->update(clientRect_);
}

void FQTermWindow::enterMenuItem() {
    char cr = CHAR_CR;
    QRect rc = session_->getMenuRect();
    FQTermSession::PageState ps = session_->getPageState();
    switch (ps) {
    case FQTermSession::Menu:
    case FQTermSession::MailMenu:
    case FQTermSession::TOP10:
        if (!rc.isEmpty()) {
            char cMenu = session_->getMenuChar();
            session_->write(&cMenu, 1);
            session_->write(&cr, 1);
        }
        break;
    case FQTermSession::ArticleList:
    case FQTermSession::BoardList:
    case FQTermSession::FriendMailList:
    case FQTermSession::EliteArticleList:
        if (!rc.isEmpty()) {
            int n = rc.y() - session_->getBuffer()->getCaretLine();
            // scroll lines
            int cursorType = n>0?FQTermSession::kDown:FQTermSession::kUp;
            n = qAbs(n);
            while (n) {
                session_->write(directions_[cursorType], 4);
                n--;
            }
        }
        session_->write(&cr, 1);
        break;
    default:
        // TODO: process other PageState.
        break;
    }
}

void FQTermWindow::processLClick(const QPoint& cellClicked) {
    int cursorType = session_->getCursorType(cellClicked);
    switch(cursorType)
    {
    case FQTermSession::kHome:
    case FQTermSession::kEnd:
    case FQTermSession::kPageUp:
    case FQTermSession::kPageDown:
        session_->writeStr(directions_[cursorType]);
        break;
    case FQTermSession::kUp:
    case FQTermSession::kDown:
    case FQTermSession::kLeft:
        session_->writeStr(directions_[cursorType]);
        break;
    case FQTermSession::kRight:  //Hand
        enterMenuItem();
        break;
    case FQTermSession::kNormal: // 2023-1-3 宋炜 移除释放鼠标左键换行操作
        //char cr = CHAR_CR;
        //session_->write(&cr, 1);
        break;
    }
}

void FQTermWindow::startSelecting(const QPoint& mousePosition) {
    // clear the selected before
    session_->clearSelect();
    sessionUpdated();

    // set the selecting flag
    isSelecting_ = true;
    lastMouseCell_ = screen_->mapToChar(mousePosition);
}

void FQTermWindow::onSelecting(const QPoint& mousePosition) {
    if (mousePosition.y() < childrenRect().top()) {
        screen_->scrollLine(-1);
    }
    if (mousePosition.y() > childrenRect().bottom()) {
        screen_->scrollLine(1);
    }

    QPoint curMouseCell = screen_->mapToChar(mousePosition);
    session_->setSelect(lastMouseCell_, curMouseCell);
    sessionUpdated();
}

void FQTermWindow::finishSelecting(const QPoint& mousePosition) {
    QPoint currentMouseCell = screen_->mapToChar(mousePosition);
    session_->setSelect(currentMouseCell, lastMouseCell_);
    refreshScreen();
    if (session_->param().isAutoCopy_) {
        copy();
    }
    isSelecting_ = false;
}

void FQTermWindow::sendKey(const int keyCode, const Qt::KeyboardModifiers modifier,
                           const QString &t) {
    if (keyCode == Qt::Key_Shift || keyCode == Qt::Key_Control
            || keyCode == Qt::Key_Meta || keyCode == Qt::Key_Alt) {
        return;
    }

    int key = keyCode;
    QString text = t;

    bool alt_on = ((modifier & Qt::MetaModifier) || (modifier & Qt::AltModifier));
    bool ctrl_on = (modifier & Qt::ControlModifier);
    bool shift_on = (modifier & Qt::ShiftModifier);

    FQ_TRACE("sendkey", 5) << "(alt " << (alt_on? " on": "off")
                           << ", ctrl " << (ctrl_on? " on": "off")
                           << ", shift " << (shift_on? " on": "off") << ")"
                           << " key: " << key
                           << " text: " << (text.size() == 0 ? "NULL" : text)
                           << " (in hex: "
                           << dumpHexString << text.toStdString()
                           << dumpNormalString << ")";

    if (session_->getBuffer()->isAnsiMode()) {
        if (session_->getBuffer()->isCursorMode()) {
            switch(key) {
            case Qt::Key_Up:
                session_->writeStr("\x1bOA");
                return;
            case Qt::Key_Down:
                session_->writeStr("\x1bOB");
                return;
            case Qt::Key_Left:
                session_->writeStr("\x1bOD");
                return;
            case Qt::Key_Right:
                session_->writeStr("\x1bOC");
                return;
            }
        } else {
            switch(key) {
            case Qt::Key_Up:
                session_->writeStr("\x1b[A");
                return;
            case Qt::Key_Down:
                session_->writeStr("\x1b[B");
                return;
            case Qt::Key_Left:
                session_->writeStr("\x1b[D");
                return;
            case Qt::Key_Right:
                session_->writeStr("\x1b[C");
                return;
            }
        }
    } else {
        // VT52 mode
        switch(key) {
        case Qt::Key_Up:
            // Do NOT change the parameter to "\x1bA".
            session_->writeStr("\x1b" "A");
            return;
        case Qt::Key_Down:
            // Do NOT change the parameter to "\x1bB".
            session_->writeStr("\x1b" "B");
            return;
        case Qt::Key_Left:
            // Do NOT change the parameter to "\x1bD".
            session_->writeStr("\x1b" "D");
            return;
        case Qt::Key_Right:
            // Do NOT change the parameter to "\x1bC".
            session_->writeStr("\x1b" "C");
            return;
        }
    }

    if (!session_->getBuffer()->isNumericMode() &&
            (modifier & Qt::KeypadModifier)) {
        switch(key) {
        case Qt::Key_0:
        case Qt::Key_1:
        case Qt::Key_2:
        case Qt::Key_3:
        case Qt::Key_4:
        case Qt::Key_5:
        case Qt::Key_6:
        case Qt::Key_7:
        case Qt::Key_8:
        case Qt::Key_9:
        {
            char tmp[] = "\x1bOp";
            tmp[2] = 'p' + key - Qt::Key_0;
            session_->writeStr(tmp);
        }
            return;
        case Qt::Key_Plus:
            session_->writeStr("\x1bOl");
            return;
        case Qt::Key_Minus:
            session_->writeStr("\x1bOm");
            return;
        case Qt::Key_Period:
            session_->writeStr("\x1bOn");
            return;
        case Qt::Key_Asterisk:
            session_->writeStr("\x1bOM");
            return;
        }
    }

    switch (key) {
    case Qt::Key_Home:
        session_->writeStr(directions_[0]);
        return;
    case Qt::Key_End:
        session_->writeStr(directions_[1]);
        return;
    case Qt::Key_PageUp:
        session_->writeStr(directions_[2]);
        return;
    case Qt::Key_PageDown:
        session_->writeStr(directions_[3]);
        return;
    case Qt::Key_Up:
        session_->writeStr(directions_[4]);
        return;
    case Qt::Key_Down:
        session_->writeStr(directions_[5]);
        return;
    case Qt::Key_Left:
        session_->writeStr(directions_[6]);
        return;
    case Qt::Key_Right:
        session_->writeStr(directions_[7]);
        return;
    case Qt::Key_F1:
        session_->writeStr("\x1b[11~");
        return;
    case Qt::Key_F2:
        session_->writeStr("\x1b[12~");
        return;
    case Qt::Key_F3:
        session_->writeStr("\x1b[13~");
        return;
    case Qt::Key_F4:
        session_->writeStr("\x1b[14~");
        return;
    case Qt::Key_F5:
        session_->writeStr("\x1b[15~");
        return;
    case Qt::Key_F6:
        session_->writeStr("\x1b[17~");
        return;
    case Qt::Key_F7:
        session_->writeStr("\x1b[18~");
        return;
    case Qt::Key_F8:
        session_->writeStr("\x1b[19~");
        return;
    case Qt::Key_F9:
        session_->writeStr("\x1b[20~");
        return;
    case Qt::Key_F10:
        session_->writeStr("\x1b[21~");
        return;
    case Qt::Key_F11:
        session_->writeStr("\x1b[23~");
        return;
    case Qt::Key_F12:
        session_->writeStr("\x1b[24~");
        return;
    }

    if (text.length() > 0) {
        if (alt_on) {
            // Use ESC to emulate Alt-XX key.
            session_->writeStr("\x1b");
        }

        if (ctrl_on) {
            switch(key) {
            case Qt::Key_At:     // Ctrl-@
            case Qt::Key_Space:  // Ctrl-SPACE
                // send NULL
                session_->write("\x0", 1);
                return;
            case Qt::Key_AsciiCircum:  // Ctrl-^
            case Qt::Key_AsciiTilde:   // Ctrl-~
            case Qt::Key_QuoteLeft:    // Ctrl-`
                // send RS
                session_->write("\x1e", 1);
                return;
            case Qt::Key_Underscore: // Ctrl-_
            case Qt::Key_Question:   // Ctrl-?
                // send US
                session_->write("\x1f", 1);
                return;
            }
        }

        if (key == Qt::Key_Backspace) {
            if (shift_on) {
                session_->writeStr("\x1b[3~");
            } else {
                if (session_->param().backspaceType_ ==  0)
                    session_->writeStr("\x08");
                else
                    session_->writeStr("\x7f");
            }
            return;
        }

        if (key == Qt::Key_Delete) {
            if (shift_on) {
                if (session_->param().backspaceType_ ==  0)
                    session_->writeStr("\x08");
                else
                    session_->writeStr("\x7f");
            } else {
                session_->writeStr("\x1b[3~");
            }
            return;
        }

        if (key == Qt::Key_Return) {
            if (session_->getBuffer()->isNewLineMode()) {
                session_->write("\r\n", 2);
            } else {
                session_->write("\r", 1);
            }
            return;
        }

        QByteArray cstrTmp = session_->unicode2bbs_smart(text);
        session_->write(cstrTmp, cstrTmp.length());
    }
    return;
}

void FQTermWindow::forcedRepaintScreen() {
    QResizeEvent *re = new QResizeEvent(screen_->size(), screen_->size());
    QApplication::postEvent(screen_, re);
}

void FQTermWindow::updateSetting(const FQTermParam& param) {
    session_->updateSetting(param);
    screen_->setTermFont(true,
                         QFont(param.englishFontName_,
                               param.englishFontSize_));
    screen_->setTermFont(false,
                         QFont(param.otherFontName_,
                               param.otherFontSize_));
    screen_->setSchema();

    setWindowTitle(param.name_);

    forcedRepaintScreen();
}

void FQTermWindow::setFont()
{
    bool isEnglish =
            ((QAction*)(sender()))->data().toBool();
    setFont(isEnglish);
}

void FQTermWindow::setFont(bool isEnglish) {
    bool ok;
    QString& fontName = isEnglish?session_->param().englishFontName_:session_->param().otherFontName_;
    int& fontSize = isEnglish?session_->param().englishFontSize_:session_->param().otherFontSize_;
    QFont font(fontName, fontSize);
    font = QFontDialog::getFont(&ok, font, this, tr("Font Selector") );
    if (ok == true) {
        if (FQTermPref::getInstance()->openAntiAlias_) {
            font.setStyleStrategy(QFont::PreferAntialias);
        }
        screen_->setTermFont(isEnglish, font);
        fontName = font.family();
        fontSize = font.pointSize();
        forcedRepaintScreen();
    }
}


void FQTermWindow::runScript(const QString &filename) {
    script_engine_->runScript(filename);
}

//-----------
static int translateQtModifiers(Qt::KeyboardModifiers modifiers) {
    int state = 0;
    if (modifiers &Qt::AltModifier) {
        state |= SBS_ALT;
    }
    if (modifiers &Qt::ControlModifier) {
        state |= SBS_CTRL;
    }
    if (modifiers &Qt::ShiftModifier) {
        state |= SBS_SHIFT;
    }
    return state;
}

static int translateQtButton(Qt::MouseButtons btnstate) {
    int state = 0;
    if(btnstate&Qt::LeftButton)
        state |= SBS_LEFT_BUTTON;
    if(btnstate&Qt::RightButton)
        state |= SBS_RIGHT_BUTTON;
    if(btnstate&Qt::MidButton)
        state |= SBS_MID_BUTTON;
    return state;
}

bool FQTermWindow::scriptKeyEvent( QKeyEvent *keyevent ){
    bool res = false;
    int state = 0;
    state |= translateQtModifiers(keyevent->modifiers());

    res = postScriptCallback(SFN_KEY_EVENT ,QScriptValueList() << SKET_KEY_PRESS << state << keyevent->key());
    return res;
}

bool FQTermWindow::scriptMouseEvent(QMouseEvent *mouseevent){
    int type = SMET_UNKOWN;
    switch (mouseevent->type())
    {
    case QEvent::MouseButtonPress:
        type = SMET_MOUSE_PRESS;
        break;
    case QEvent::MouseButtonRelease:
        type = SMET_MOUSE_RELEASE;
        break;
    case QEvent::MouseButtonDblClick:
        type = SMET_MOUSE_DBCLICK;
        break;
    case QEvent::MouseMove:
        type = SMET_MOUSE_MOVE;
        break;
    default:
        // TODO: what about others?
        break;
    }

    int delta = 0;
    int state=0;

    state |= translateQtButton(mouseevent->button());
    state |= translateQtModifiers(mouseevent->modifiers());

    QPoint ptc = screen_->mapToChar(mouseevent->pos());
    ptc.setY(ptc.y() - screen_->getBufferStart());
    int res = postScriptCallback(SFN_MOUSE_EVENT
                             #ifdef HAVE_PYTHON
                                 ,Py_BuildValue("liiiii", this, type, state, ptc.x(), ptc.y(),delta)
                             #endif //HAVE_PYTHON
                                 ,QScriptValueList() << type << state << ptc.x() << ptc.y() << delta);
    return res;
}

bool FQTermWindow::scriptWheelEvent( QWheelEvent *wheelevent ) {
    int type = SMET_WHEEL;
    int state=0;
    state |= translateQtModifiers(wheelevent->modifiers());
    QPoint ptc = screen_->mapToChar(wheelevent->pos());
    ptc.setY(ptc.y() - screen_->getBufferStart());
    int res = postScriptCallback(SFN_MOUSE_EVENT, QScriptValueList() << type << state << ptc.x() << ptc.y() << wheelevent->delta());
    return res;
}

const QString FQTermExternalEditor::textEditName_ =  "external editor input";

FQTermExternalEditor::FQTermExternalEditor(QWidget* parent) 
    : QObject(parent),
      started_(false) {
    editorProcess_ = new QProcess(this);
    FQ_VERIFY(connect(editorProcess_, SIGNAL(stateChanged(QProcess::ProcessState)), this, SLOT(stateChanged(QProcess::ProcessState))));
    clearTempFileContent();
}  

FQTermExternalEditor::~FQTermExternalEditor() {
    editorProcess_->kill();
    QFile::remove(getTempFilename());
    delete editorProcess_;
}

void FQTermExternalEditor::execDialog() {
    QDialog *dialog = new QDialog((QWidget *)parent());
    QGridLayout *layout = new QGridLayout(dialog);
    QTextEdit *text = new QTextEdit(dialog);
    text->setObjectName(textEditName_);
    layout->addWidget(text);
    QBoxLayout  *rowLayout = new QBoxLayout(QBoxLayout::LeftToRight, dialog);
    layout->addLayout(rowLayout, 1, 0, Qt::AlignHCenter);
    QPushButton *ok = new QPushButton(tr("OK"), dialog);
    QPushButton *cancel = new QPushButton(tr("Cancel"), dialog);
    rowLayout->addWidget(ok);
    rowLayout->addWidget(cancel);
    FQ_VERIFY(connect(ok, SIGNAL(clicked()),
                      dialog, SLOT(accept())));
    FQ_VERIFY(connect(cancel, SIGNAL(clicked()),
                      dialog, SLOT(reject())));
    FQ_VERIFY(connect(dialog, SIGNAL(accepted()),
                      this, SLOT(readDialogData())));
    FQ_VERIFY(connect(dialog, SIGNAL(rejected()),
                      this, SLOT(closeDialog())));
    dialog->setModal(true);
    dialog->exec();
}

void FQTermExternalEditor::readDialogData() {
    QDialog *dialog = (QDialog*)sender();
    QTextEdit* edit = (QTextEdit*)dialog->findChild<QTextEdit*>(textEditName_);
    if (edit) {
        emit done(edit->toPlainText());
    }
    closeDialog();
}

void FQTermExternalEditor::closeDialog() {
    sender()->deleteLater();
}

void FQTermExternalEditor::start() {
    if (FQTermPref::getInstance()->externalEditor_.isEmpty()) {
        execDialog();
        return;
    }
    if (started_)
        return;
    started_ = true;
    if (FQTermPref::getInstance()->externalEditorArg_.isEmpty()) {
        editorProcess_->start(FQTermPref::getInstance()->externalEditor_, QStringList() << getTempFilename());
    } else {
        FQ_TRACE("editor", 5) << FQTermPref::getInstance()->externalEditorArg_.arg(getTempFilename());
        editorProcess_->start(FQTermPref::getInstance()->externalEditor_ + " " + FQTermPref::getInstance()->externalEditorArg_.arg(getTempFilename()));
    }
}


QString FQTermExternalEditor::getTempFilename() {
#ifndef WIN32
    return "/tmp/.fqterm_tmp.txt";
#else
    return getPath(USER_CONFIG) + "tmp_do_not_use.txt";
#endif
}

void FQTermExternalEditor::clearTempFileContent() {
    QFile file(getTempFilename());
    file.open(QIODevice::Truncate | QIODevice::WriteOnly);
    file.write("\xEF\xBB\xBF");
    file.flush();
    file.close();
}

void FQTermExternalEditor::stateChanged( QProcess::ProcessState state ) {
    //FQ_TRACE("editor", 0) << "stateChanged: " << state << editorProcess_->readAllStandardOutput() << editorProcess_->readAllStandardError();
    if (!started_ || state != QProcess::NotRunning)
        return;
    QFile file(getTempFilename());
    file.open(QIODevice::ReadOnly);
    QString result = U82U(file.readAll());
    file.close();
#if !defined(WIN32) && !defined(__APPLE__) 
    if (result.endsWith(OS_NEW_LINE)) {
        result.resize(result.size() - 1);
    }
#endif
    emit done(result);
    clearTempFileContent();
    started_ = false;
}
} // namespace FQTerm
#include "fqterm_window.moc"
